package hasthi.metamodel.resource;

import hasthi.common.ChangeListener;
import hasthi.common.HasthiException;
import hasthi.common.InvaliedResourceDataException;
import hasthi.common.constants.WSDMConstants;
import hasthi.common.constants.WSDMConstants.SystemState;
import hasthi.coordinator.RemoteManagerContext;
import hasthi.manager.ManagedResourceType;
import hasthi.metamodel.ResourceUtils;
import static hasthi.common.constants.HasthiConstants.LOGGER_NAME;
import static hasthi.common.constants.WSDMConstants.*;

import java.util.Collection;
import java.util.Map;

import javax.xml.namespace.QName;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Keep a Summery (Selected set of parameters of the resource). This is usually hold by the 
 * Coordinator. This Guy does not need a model listener as the changes are not needed to send to someone else. 
 * )
 * @author Srinath Perera(hperera@cs.indiana.edu)
 */

public class SummerizedManagedResource extends AbstractManagedResource {
    private static Log log = LogFactory.getLog(LOGGER_NAME);
    private String holderURL;
    
    SummerizedManagedResource(QName name, ResourceModelContext modelContext, String managerAgentURL, String category, String userDefinedType) throws InvaliedResourceDataException {
        super(name, modelContext, managerAgentURL, category, userDefinedType);
        this.fullcopy = false;
    }
    
    SummerizedManagedResource(ResourceModelContext modelContext,ManagedResourceType resource) throws InvaliedResourceDataException {
        super(modelContext,resource);
        this.fullcopy = false;
    }
    
    SummerizedManagedResource(ResourceModelContext modelContext,Map<QName, Object> extractedProperties,String managerAgentURL) throws InvaliedResourceDataException {
        super(modelContext,extractedProperties,managerAgentURL);
        this.fullcopy = false;
    }

    public String getProperty(String name) throws HasthiException {
        if(ResourceUtils.isIncludedInSummerized(name)){            
            return properties.get(name);
        }else{
            if(System.currentTimeMillis() - lastUpdated > 30000){
                fullcopy = false;
            }else{
                String value = properties.get(name);
                if(value != null){
                    return value;
                }   
            }
            
            if(!fullcopy){
                readAllProperties();
            }
            return properties.get(name);
        }
    }
    
    /**
     * We set the property only if it's new value is different from old value
     * @param name
     * @param value
     * @throws HasthiException 
     */
    public void setProperty(String name,Object value) throws HasthiException{
        String strValue = modelContext.getConverter().convert(value);
        String oldValue = properties.get(name);
        if(strValue == null ||  !strValue.equals(oldValue)){
            ChangeListener changeListener = modelContext.getChangeListener();
            if(changeListener != null){
                changeListener.propertyChanged(this.name, new QName(name), value);
            }
            properties.put(name, strValue);
        }
    }

    public void setState(SystemState state) throws HasthiException {
        String name = RESOURCE_STATUS_PARAM.getLocalPart();
        setProperty(name, state.toString());    
    }
    @Override
    public Collection<String> getProperties() {
        //TODO handle list properties (
        return super.getProperties();
    }
    
    
    public void readAllProperties() throws HasthiException{
        if(holderURL == null){
            //if this is the coordinator, find holder URL directly
            if(modelContext.getCoordinatorContext() != null){
                RemoteManagerContext managerForResource = modelContext.getCoordinatorContext().getManagerForResource(getQName());
                if(managerForResource != null){
                    holderURL = managerForResource.getAddress().getSoapAddress();    
                }
            }
            if(holderURL == null){
                //ask coordinator for the holder
                if(modelContext.getCoordinatorUrl() != null){
                    holderURL = ResourceUtils.fetchHolderURL(modelContext, this.name);
                }else{
                    throw new HasthiException("Looking for resource "+getName()+ ", coordiantor not found", HasthiException.FaultCode.InternalServiceError);
                }
            }
        }
        if(holderURL == null){
            throw new HasthiException("Resource "+getName()+ " not found in the coordiantor ", HasthiException.FaultCode.InternalServiceError);
        }
        try {
            Map<QName,Object> parameters = ResourceUtils.fetchRemoteResourceParameters(modelContext,holderURL,this.name);
            for(QName key:parameters.keySet()){
                Object value = parameters.get(key);
                String strValue = modelContext.getConverter().convert(value);
                properties.put(key.getLocalPart(), strValue);
            }
            fullcopy = true;
            lastUpdated = System.currentTimeMillis();
        } catch (HasthiException e) {
            log.warn("Looking for resource "+ getName() + " but did not find it in assigned manager.", e);
            //this typically happen when resource has been removed from the resource. So we log and let it pass
        }
    }
    
}
